<?php
// +-------------+------------------+------+-----+---------+----------------+
// | Field       | Type             | Null | Key | Default | Extra          |
// +-------------+------------------+------+-----+---------+----------------+
// | id          | int(11)          | NO   | PRI | NULL    | auto_increment |
// | name        | varchar(80)      | YES  |     |         |                |
// | title       | varchar(30)      | YES  |     |         |                |
// | status      | tinyint(1)       | YES  |     | 1       |                |
// | condition   | varchar(200)     | YES  |     |         |                |
// | icon        | varchar(50)      | YES  |     |         |                |
// | islink      | tinyint(1)       | YES  |     | 0       |                |
// | sort        | int(11)          | YES  |     | 0       |                |
// | tips        | varchar(50)      | YES  |     |         |                |
// | member_id   | int(11) unsigned | YES  |     | 0       |                |
// | create_time | int(11) unsigned | YES  |     | 0       |                |
// | update_time | int(11) unsigned | YES  |     | 0       |                |
// | parent_id   | int(11) unsigned | YES  |     | 0       |                |
// | controller  | varchar(50)      | YES  |     |         |                |
// | action      | varchar(50)      | YES  |     |         |                |
// +-------------+------------------+------+-----+---------+----------------+
namespace app\admin\model;

use think\facade\Cache;
use think\facade\Session;
use app\admin\model\Base;
use app\admin\model\AuthGroup as GroupModel;
use app\admin\validate\AuthRule as Validate;
class AuthRule extends Base
{

    protected static function init()
    {
        //模型事件只在调用模型的方法生效，使用查询构造器操作是无效的
        self::afterDelete(function ($user) {
            $model = new AuthRule;
            $model->clearMenuCache();
        });
    }

    public function parent()
    {
        return $this->belongsTo('AuthRule', 'parent_id');
    }

    public function children()
    {
        return $this->hasMany('AuthRule', 'parent_id');
    }

    /**
     * 树状结构菜单
     * @Author   zhanghong(Laifuzi)
     * @DateTime 2018-06-07
     * @param  integer    $parent_id 根结点ID
     * @param  boolean    $skip_hide 是否忽略隐藏结点
     * @param  integer    $level     结点深度
     * @return array                 Tree
     */
    public function findWithDescendants($parent_id = 0, $skip_hide = true, $allow_ids = NULL, $level = 0)
    {
        $children = $this->selectChildren($parent_id, $skip_hide, $allow_ids);
        if(!empty($children)){
            foreach ($children as $key => &$child) {
                $child["deep_level"] = $level;
                $child["children"] = $this->findWithDescendants($child["id"], $skip_hide, $allow_ids, $level+1);
            }
        }
        return $children;
    }

    /**
     * 获取后台菜单的树奖结构(所有菜单项)
     * @Author   zhanghong(Laifuzi)
     * @DateTime 2018-06-07
     * @return   array
     */
    public function allTreeMenu(){
        $cache_key = $this->cacheKeyOfTree();
        $menu = Cache::get($cache_key);
        if(!empty($menu)){
            return $menu;
        }
        $menu = $this->findWithDescendants(0);
        Cache::set($cache_key, $menu);
        return $menu;
    }

    /**
     * 用户的左侧菜单项(树状结构)
     * @Author   zhanghong(Laifuzi)
     * @DateTime 2018-06-08
     * @return   [type]             [description]
     */
    public function memberTreeMenu(){
        $cache_key = $this->cacheKeyOfTree();
        if(Session::has($cache_key)){
            return Session::get($cache_key);
        }

        $current_member = login_member();
        if(empty($current_member)){
            return [];
        }
        if($current_member['is_super_group']){
            $allow_ids = NULL;
        }else{
            // 当前用户没有对应的管理员组或管理员组没有设置可访问菜单
            $group = GroupModel::get(intval($current_member['group_id']));
            if(empty($group) || empty($group['rules'])){
                return [];
            }
            $allow_ids = explode(',', $group['rules']);
        }
        $menu = $this->findWithDescendants(0, true, $allow_ids);
        if(!empty($menu)){
            Session::set($cache_key, $menu);
        }
        return $menu;
    }

    /**
     * 一维数组结构菜单
     * @Author   zhanghong(Laifuzi)
     * @DateTime 2018-06-07
     * @param  array      $list      保存菜单的数组
     * @param  integer    $parent_id 根结点ID
     * @param  array      $ancestors 所有祖先元素
     * @param  string     $prefix       显示名前辍
     * @return array                    Rule集合
     */
    public function explandWithDescendants($list, $parent_id = 0, $allow_ids = NULL, $ancestors = [], $prefix="")
    {
        $children = $this->selectChildren($parent_id, false, $allow_ids);
        if(!empty($children)){
            //如果有子类
            $level = count($ancestors);
            foreach ($children as $key => $child) {
                $child['deep_level'] = $level;
                $child['shown_title'] = $prefix.$child['title'];
                if(empty($child['name'])){
                    $key = 'rule_'.$child['id'];
                }else{
                    $key = $child['name'];
                }
                $list[$key] = $child;

                $sub_ancestors = $ancestors;
                $sub_ancestors[$child['id']] = $child['title'];
                //所有祖先结点和自己的ID列表
                $child["with_ancestors"] = $sub_ancestors;
                if($level == 0){
                    //第二级菜单
                    $sub_prefix = "┗━";
                }else if($level > 0){
                    //第二级以后的菜单
                    $sub_prefix = '&nbsp;&nbsp;&nbsp;&nbsp;'.$prefix;
                }
                $list = $this->explandWithDescendants($list, $child["id"], $allow_ids, $sub_ancestors, $sub_prefix);
            }
        }
        return $list;
    }

    /**
     * 获取后台菜单的数组结构(所有菜单项)
     * @Author   zhanghong(Laifuzi)
     * @DateTime 2018-06-07
     * @return   array
     */
    public function allListMenu(){
        $cache_key = $this->cacheKeyOfList();
        $menu = Cache::get($cache_key);
        if(!empty($menu)){
            return $menu;
        }
        $menu = $this->explandWithDescendants([], 0);
        Cache::set($cache_key, $menu);
        return $menu;
    }

    /**
     * 用户拥有访问权限的菜单（一维数组）
     * @Author   zhanghong(Laifuzi)
     * @DateTime 2018-06-08
     * @return   [type]             [description]
     */
    public function memberListMenu(){
        $cache_key = $this->cacheKeyOfList();
        if(Session::has($cache_key)){
            return Session::get($cache_key);
        }

        $current_member = login_member();
        if(empty($current_member)){
            return [];
        }

        if($current_member['is_super_group']){
            $allow_ids = NULL;
        }else{
            // 当前用户没有对应的管理员组或管理员组没有设置可访问菜单
            $group = GroupModel::get(intval($current_member['group_id']));
            if(empty($group) || empty($group['rules'])){
                return [];
            }
            $allow_ids = explode(',', $group['rules']);
        }

        $menu = $this->explandWithDescendants([], 0, $allow_ids);
        if(!empty($menu)){
            Session::set($cache_key, $menu);
        }
        return $menu;
    }

    /**
     * 判断用户是否拥有当前页面的访问权限
     * @Author   zhanghong(Laifuzi)
     * @DateTime 2018-06-08
     * @param    string             $act_name   Action Name
     * @param    string             $ctrl_name  Controlelr Name
     * @return   array/NULL
     */
    public function checkCurrentPage($act_name = '', $ctrl_name = ''){
        $current_member = login_member();
        if(empty($current_member)){
            return NULL;
        }

        $request = request();
        if(empty($ctrl_name)){
            $ctrl_name = $request->controller();
        }
        if(empty($act_name)){
            $act_name = $request->action();
        }
        $menu_name = $ctrl_name."/".$act_name;

        $expland_menu = $this->memberListMenu();
        if(isset($expland_menu[$menu_name])){
            $current_page = $expland_menu[$menu_name];
        }else{
            $current_page = [];
        }
        if($current_member['is_super_group']){
          return $current_page;
        }else if(in_array($ctrl_name, array('Index', 'Upload'))){
          return $current_page;
        }else if(in_array($act_name, array('demo', 'test'))){
          return $current_page;
        }else if(stripos($act_name, 'ajax_') === 0){
          return $current_page;
        }else if(!empty($current_page)){
          return $current_page;
        }
        return NULL;
    }

    /**
     * 查看是否拥有指定页面的访问权限
     * @Author   zhanghong(Laifuzi)
     * @DateTime 2018-06-08
     * @param    string             $act_name   Action Name
     * @param    string             $ctrl_name  Controlelr Name
     * @return   boolean                        [description]
     */
    public function check($act_name = '', $ctrl_name = ''){
        $page = $this->checkCurrentPage($act_name, $ctrl_name);
        if(is_null($page)){
            return false;
        }else{
            return true;
        }
    }

    /**
     * 清除菜单缓存数据
     * @Author   zhanghong(Laifuzi)
     * @DateTime 2018-06-07
     * @return   boolean
     */
    private function clearMenuCache(){
        $cache_key = $this->cacheKeyOfTree();
        Cache::rm($cache_key);
        $cache_key = $this->cacheKeyOfList();
        Cache::rm($cache_key);
        return true;
    }

    /**
     * 当用户退出登录时清空缓存的用户菜单信息
     * @Author   zhanghong(Laifuzi)
     * @DateTime 2018-06-08
     * @return   [type]             [description]
     */
    public function clearMemberSession(){
        $cache_key = $this->cacheKeyOfTree();
        Session::delete($cache_key);
        $cache_key = $this->cacheKeyOfList();
        Session::delete($cache_key);
        return true;
    }

    /**
     * 添加或更新记录
     * @Author   zhanghong(Laifuzi)
     * @DateTime 2018-06-07
     * @param    array              $data 表单提交数据
     * @param    integer            $id   主键ID
     * @return   array                    更新结果
     */
    public function createOrUpdate($data, $id = 0)
    {
        $res = ['status' => false, 'data' => [], 'msg' => "添加或更后台菜单失败"];
        if($id > 0){
            $data['id'] = $id;
        }

        if(isset($data['controller']) && isset($data['action'])){
            if(empty($data['controller']) || empty($data['action'])){
                $data['name'] = '';
            }else{
                $data['name'] = $data['controller'].'/'.$data['action'];
            }
        }

        $validate = new Validate;
        if(!$validate->check($data)){
            $res['msg'] = $validate->getError();
            return $res;
        }

        if(isset($data['id']) && $data['id'] > 0){
            $item = $this->where('id', $data['id'])->find();
        }else{
            $item = [];
        }
        if(empty($item)){
            $is_update = false;
            $msg = '添加后台菜单成功';
        }else{
            $is_update = true;
            $msg = '更新后台菜单成功';
            $data['id'] = $item['id'];
        }
        $this->data($data)->isUpdate($is_update)->allowField(true)->save();

        $this->clearMenuCache();

        $res = [
            'status' => true,
            'msg' => $msg,
            'data' => ['id' => $this->id],
        ];
        return $res;
    }

    /**
     * 根据菜单名或标题创建/更新记录
     * @Author   zhanghong(Laifuzi)
     * @DateTime 2018-06-07
     * @param    array              $data [description]
     * @return   array                    [description]
     */
    public function findOrCreate($data)
    {
        $res = ['status' => false, 'data' => [], 'msg' => "添加或更新后台菜单失败"];

        $map = [];
        if(empty($data['name'])){
            $map['title'] = $data['title'];
        }else{
            $map['name'] = $data['name'];
        }
        $item = $this->where($map)->find();
        if(!empty($item)){
            $res = [
                'status' => true,
                'data' => ['id' => $item['id']],
                'msg' => '查找到该后台菜单记录',
            ];
            return $res;
        }

        return $this->createOrUpdate($data);
    }

    // /**
    //  * 删除记录
    //  * @Author   zhanghong(Laifuzi)
    //  * @DateTime 2018-06-07
    //  * @param    integer            $id [description]
    //  * @return   array                  [description]
    //  */
    // public function deleteItem($id){
    //     $res = ['status' => false, 'data' => [], 'msg' => '删除后台菜单1'];

    //     $id = intval($id);
    //     $count = $this->where('parent_id', $id)->count();
    //     if($count){
    //         $res['msg'] = '有子菜单的记录不能删除';
    //         return $res;
    //     }

    //     if($this->where('id', $id)->delete()){
    //         $res = [
    //             'status' => true,
    //             'msg' => '删除后台菜单成功',
    //             'data' => ['id' => $id],
    //         ];
    //         $this->clearMenuCache();
    //     }else{
    //         $res['msg'] = '未找到删除记录';
    //     }

    //     return $res;
    // }

    /**
     * 验证是否可以删除当前记录
     * @Author   zhanghong(Laifuzi)
     * @DateTime 2018-06-12
     * @param    integer            $id [description]
     * @return   [type]                 [description]
     */
    protected function canDeleteItem($id){
        $res = ['status' => false, 'msg' => '不可以删除当前菜单项'];
        $id = intval($id);
        $item = $this->where('id', $id)->find();
        if(empty($item)){
            $res['msg'] = '删除菜单项不存在';
            return $res;
        }
        if($item->children()->count()){
            $res['msg'] = '不能删除有子菜单的记录';
            return $res;
        }

        $res = ['status' => true, 'msg' => '可以删除当前分类'];
        return $res;
    }

    /**
     * 删除记录后的执行方法
     * @Author   zhanghong(Laifuzi)
     * @DateTime 2018-06-12
     * @param    array              $item [description]
     * @return   boolean                  [description]
     */
    protected function afterDeleteItem($item){
        $this->clearMenuCache();
        return true;
    }

    /**
     * 排序后的操作
     * @Author   zhanghong(Laifuzi)
     * @DateTime 2018-06-11
     * @return   boolean                   [description]
     */
    protected function afterResort(){
        $this->clearMenuCache();
    }

    private function cacheKeyOfTree(){
        return 'auth_rule_menu_tree';
    }

    private function cacheKeyOfList(){
        return 'auth_rule_menu_list';
    }

    /**
     * 查询子结点
     * @Author   zhanghong(Laifuzi)
     * @DateTime 2018-06-07
     * @param    integer            $parent_id Parent ID
     * @param    boolean            $skip_hide 是否忽略隐藏结点
     * @return   array                         AuthRule Item List
     */
    private function selectChildren($parent_id, $skip_hide = true, $allow_ids = NULL)
    {
        $map = [
            'parent_id' => $parent_id,
        ];
        if($skip_hide){
            $map['status'] = 1;
        }
        if(is_array($allow_ids)){
            $map['id'] = $allow_ids;
        }
        // return $map;
        $order_str = $this->queryOrderStr();
        $select_fields = [
            'id', 'name', 'controler'
        ];
        return $this->where($map)->order($order_str)->select();
    }

    /**
     * 搜索排序方式
     * @Author   zhanghong(Laifuzi)
     * @DateTime 2018-06-07
     * @param    string             $param 排序方式
     * @return   string                    [description]
     */
    protected function queryOrderStr($order_type = '')
    {
        return 'sort ASC, id ASC';
    }

    /**
     * 唯一值字段列表
     * @Author   zhanghong(Laifuzi)
     * @DateTime 2018-06-07
     * @return   array             [description]
     */
    protected function uniqueFileList()
    {
        return ['name', 'title'];
    }
}
